/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package iagente;



import java.util.Arrays;
import java.util.Random;
/**
 *
 * @author Note
 */
public class MatrizQ {
   

	//Atributos del mapa
	public static final char AGENTE = 'R';
	public static final char FINAL = 'G';
	public static final char VIRTUAL_ROBOT = 'V';
	public static final char PARED = '#';
	public static final char NEUTRO = ' ';
	public static final char LLEGADAFINAL = 'H';
        public static final char MALA = 'M';
        public static final char BUENA = 'B';
        public static final char EXCELENTE = 'E';
      
	
	// Estados posibles del mapa en la simulacion
	public static boolean llegada;
        public static boolean peligro;
	public static boolean llegada_vr;
	private boolean peligro_vr;
        
        
	
	// Direcciones posibles
	public static final int arriba    = 0;
        public static final int arribaizq   = 1;
        public static final int arribader   = 2;
	public static final int abajo  = 3;
        public static final int abajoizq = 4;
        public static final int abajoder = 5;
	public static final int izquierda  = 6;
	public static final int derecha = 7;
	
	// Atributos de la matriz Qlearning
	private final double QGAMMA = Interfaz.gamma;
	
	private final int RFINAL = Interfaz.estadofinal;
	private final int RPOZO = Interfaz.pozo;
        private int REXCELENTE = Interfaz.excelente;
        private int RBUENA = Interfaz.buena;
        private int RMALA =  Interfaz.mala;
	
	// Un generador de numeros aleatorios
	private Random randomGenerator;

	// Matriz virtual para el aprendizaje
	private char[] v_map;
	
	// Matriz de recompenzas Q
	private double[] q_matriz;
	
	// Array for giving reward to our states
	public static MatrizR matrec; 
        
        public static MatrizQ mq = null;
        
        public static boolean aprendio = false;
        
        public static int tiempolimite = 30000;
        
    public MatrizQ(){
                
                v_map		= new char[Interfaz.tamaño];
		q_matriz	= new double[Interfaz.tamaño];
                randomGenerator = new Random();
                llegada		 = false;
		llegada_vr	 = false;
		peligro	 = false;
		peligro_vr = false;
                matrec = new MatrizR();
                
                
}
  
    public static MatrizQ getInstance(){
        if(mq == null)
            
            mq = new MatrizQ();
        
            return mq;
    }
    
    
    // Copy real map to virtual map and initialize.
    private void initVirtualMap()
    {
       for(int i = 0; i < Interfaz.tamaño; i++)
		{
			if(Interfaz.getMapa().map[i] == AGENTE)
				v_map[i] = NEUTRO;
			
			else
				v_map[i] = Interfaz.getMapa().map[i];
		}

        }

    
    
    // Generate random index for the initial state of the virtual robot
	private void initVirtualRobot()
	{
		llegada_vr = false;
		peligro_vr = false;
		
		int index = randomGenerator.nextInt(Interfaz.tamaño);
                
               
                
             
               while(v_map[index] == PARED)
                    index = randomGenerator.nextInt(Interfaz.tamaño);          
                    v_map[index] = VIRTUAL_ROBOT;
                     
                   
	}
	
  
  // Get robot index
    public int getRobotIndex()
	{
		for(int i = 0; i < Interfaz.tamaño; i++)
			if(Interfaz.getMapa().map[i] == AGENTE)
				return i;
		
		return -1; // Robot not found!!
	}
    
    // Get virtual robot index
	// If virtual robot not found it returns -1
	public int getVirtualRobotIndex()
	{
		for(int i = 0; i < Interfaz.tamaño; i++)
			if(v_map[i] == VIRTUAL_ROBOT)
				return i;
		
		return -1; // Virtual Robot not found!!
	}
        
        
        
	// Virtual Robot MOVE UP
	private boolean moveUpVR()
	{
		int index = getVirtualRobotIndex();
		
		if(index == -1) return false;
		
		int destination = index - Interfaz.x_fila;
		
		if(v_map[destination] != PARED )
		{
			// Check GOAL and DANGER states
			if(v_map[destination] == PARED) peligro_vr = true;
			else if(v_map[destination] == FINAL) llegada_vr = true;
			
			v_map[index] = NEUTRO; // Clear previous coordinate
			v_map[destination] = VIRTUAL_ROBOT; // Update new coordinate
			return true;
		}
		
		return false;
	}
	
         // Virtual Robot MOVE UP/LEFT
	private boolean moveUpLeftVR()
	{
		int index = getVirtualRobotIndex();
		
		if(index == -1) return false;
		
		int destination = index - Interfaz.x_fila -1;
		
		if( v_map[destination] != PARED )
		{
			// Check GOAL and DANGER states
			if(v_map[destination] == PARED) peligro_vr = true;
			else if(v_map[destination] == FINAL) llegada_vr = true;
			
			v_map[index] = NEUTRO; // Clear previous coordinate
			v_map[destination] = VIRTUAL_ROBOT; // Update new coordinate
			return true;
		}
		
		return false;
	}
	
        // Virtual Robot MOVE UP/RIGHT
	private boolean moveUpRightVR()
	{
		int index = getVirtualRobotIndex();
		
		if(index == -1) return false;
		
		int destination = index - Interfaz.x_fila + 1;
		
		if(v_map[destination] != PARED)
		{
			// Check GOAL and DANGER states
			if(v_map[destination] == PARED) peligro_vr = true;
			else if(v_map[destination] == FINAL) llegada_vr = true;
			
			v_map[index] = NEUTRO; // Clear previous coordinate
			v_map[destination] = VIRTUAL_ROBOT; // Update new coordinate
			return true;
		}
		
		return false;
	}
        
	
	// Virtual Robot MOVE DOWN
	private boolean moveDownVR()
	{
		int index = getVirtualRobotIndex();
		
		if(index == -1) return false;
		
		int destination = index + Interfaz.x_fila;
		
		if(v_map[destination] != PARED)
		{
			// Check GOAL and DANGER states
			if(v_map[destination] == PARED) peligro_vr = true;
			else if(v_map[destination] == FINAL) llegada_vr = true;
			
			v_map[index] = NEUTRO; // Clear previous coordinate
			v_map[destination] = VIRTUAL_ROBOT; // Update new coordinate
			return true;
		}
		
		return false;
	}
        
         // Virtual Robot MOVE DOWN/LEFT
	private boolean moveDownLeftVR()
	{
		int index = getVirtualRobotIndex();
		
		if(index == -1) return false;
		
		int destination = index + Interfaz.x_fila -1;
		
		if(v_map[destination] != PARED)
		{
			// Check GOAL and DANGER states
			if(v_map[destination] == PARED) peligro_vr = true;
			else if(v_map[destination] == FINAL) llegada_vr = true;
			
			v_map[index] = NEUTRO; // Clear previous coordinate
			v_map[destination] = VIRTUAL_ROBOT; // Update new coordinate
			return true;
		}
		
		return false;
	}
        
        // Virtual Robot MOVE DOWN/RIGHT
	private boolean moveDownRightVR()
	{
		int index = getVirtualRobotIndex();
		
		if(index == -1) return false;
		
		int destination = index + Interfaz.x_fila +1;
		
		if(v_map[destination] != PARED)
		{
			// Check GOAL and DANGER states
			if(v_map[destination] == PARED) peligro_vr = true;
			else if(v_map[destination] == FINAL) llegada_vr = true;
			
			v_map[index] = NEUTRO; // Clear previous coordinate
			v_map[destination] = VIRTUAL_ROBOT; // Update new coordinate
			return true;
		}
		
		return false;
	}
	
	
	
	// Virtual Robot MOVE LEFT
	private boolean moveLeftVR()
	{
		int index = getVirtualRobotIndex();
		
		if(index == -1) return false;
		
		int destination = index - 1;
		
		if(v_map[destination] != PARED )
		{
			// Check GOAL and DANGER states
			if(v_map[destination] == PARED) peligro_vr = true;
			else if(v_map[destination] == FINAL) llegada_vr = true;
			
			v_map[index] = NEUTRO; // Clear previous coordinate
			v_map[destination] = VIRTUAL_ROBOT; // Update new coordinate
			return true;
		}
		
		return false;
	}
	
	
        
        
	// Virtual Robot MOVE RIGHT
	private boolean moveRightVR()
	{
		int index = getVirtualRobotIndex();
		
		if(index == -1) return false;
		
		int destination = index + 1;
		
		if(v_map[destination] != PARED)
		{
			// Check GOAL and DANGER states
			if(v_map[destination] == PARED) peligro_vr = true;
			else if(v_map[destination] == FINAL) llegada_vr = true;
			
			v_map[index] = NEUTRO; // Clear previous coordinate
			v_map[destination] = VIRTUAL_ROBOT; // Update new coordinate
			return true;
		}
		
		return false;
	}
        
        // Assigns 0's to our QMatrix
	private void initQMatrix()
	{
		for(int i = 0; i < Interfaz.tamaño; i++)
			q_matriz[i] = 0;	
	}
        
        // Moves virtual robot randomly
	private void RandomMove()
	{
		boolean isMoved = false;
		
		while(!isMoved)
		{
			int choice = randomGenerator.nextInt(8);
			
			if(choice == arriba)
			{
				if(moveUpVR())
					isMoved = true;
			}
                        
                        else if (choice == arribaizq)
			{
				if(moveDownVR())
					isMoved = true;
			}
                        
                        else if (choice == arribader)
			{
				if(moveDownVR())
					isMoved = true;
			}
                        
                        else if (choice == abajo)
			{
				if(moveDownVR())
					isMoved = true;
			}
                        else if (choice == abajoizq)
			{
				if(moveDownVR())
					isMoved = true;
			}
                        
                        else if (choice == abajoder)
			{
				if(moveDownVR())
					isMoved = true;
			}
                        
                        

			else if (choice == izquierda)
			{
				if(moveLeftVR())
					isMoved = true;
			}

			else if (choice == derecha)
			{
				if(moveRightVR())
					isMoved = true;
			}
		}
	}
	
        public void moverG(int choice){
            
            boolean isMoved = false;
            if(choice == arriba)
			{
				if(moveUpVR())
					isMoved = true;
			}
                        
                        else if (choice == arribaizq)
			{
				if(moveDownVR())
					isMoved = true;
			}
                        
                        else if (choice == arribader)
			{
				if(moveDownVR())
					isMoved = true;
			}
                        
                        else if (choice == abajo)
			{
				if(moveDownVR())
					isMoved = true;
			}
                        else if (choice == abajoizq)
			{
				if(moveDownVR())
					isMoved = true;
			}
                        
                        else if (choice == abajoder)
			{
				if(moveDownVR())
					isMoved = true;
			}
                        
                        

			else if (choice == izquierda)
			{
				if(moveLeftVR())
					isMoved = true;
			}

			else if (choice == derecha)
			{
				if(moveRightVR())
					isMoved = true;
			}
     }
	
        
        
        public void eGreedy(){
            
            double ale = Math.random();
            
            if ( ale < Interfaz.eptau){
                
                RandomMove();
               
            }else {
                int VRindex = getVirtualRobotIndex();
                int movimiento=-1;
                double maximaRecompensa=0;
                for(int i = 0; i < 7; i++)
		{
                    int destination = 0;

                    if(i == arriba)	    destination = VRindex - Interfaz.x_fila; 
                    else if(i == arribaizq)   destination = VRindex - Interfaz.x_fila -1;
                    else if(i == arribader)   destination = VRindex - Interfaz.x_fila +1;
                    else if(i == abajo)  destination = VRindex + Interfaz.x_fila;
                    else if(i == abajoizq) destination = VRindex + Interfaz.x_fila -1;
                    else if(i == abajoder) destination = VRindex + Interfaz.x_fila + 1;
                    else if(i == izquierda)  destination = VRindex - 1;
                    else if(i == derecha) destination = VRindex + 1;

                    if(v_map[destination] != MatrizQ.PARED){
                        if(q_matriz[destination]>maximaRecompensa){
                            maximaRecompensa= q_matriz[destination];
                            movimiento= destination;
                        }
                    }
		}
                moverG(movimiento);          
              }
        }
        
        public void softMax(){
            
            
             int VRindex = getVirtualRobotIndex();
                int movimiento=-1;
                
                
              //Busco los posibles movimientos desde la posicion en la que estoy  
                int[] posiblesmovimientos = null;  
                double tot;
                double[] probabilidades = null;
                int j = 0;
                for(int i = 0; i < 7; i++)
		{
                    int destino = 0;

                    if(i == arriba)	      destino = VRindex - Interfaz.x_fila; 
                    else if(i == arribaizq)   destino = VRindex - Interfaz.x_fila -1;
                    else if(i == arribader)   destino = VRindex - Interfaz.x_fila +1;
                    else if(i == abajo)       destino = VRindex + Interfaz.x_fila;
                    else if(i == abajoizq)    destino = VRindex + Interfaz.x_fila -1;
                    else if(i == abajoder)    destino = VRindex + Interfaz.x_fila + 1;
                    else if(i == izquierda)   destino = VRindex - 1;
                    else if(i == derecha)     destino = VRindex + 1;

                    if(v_map[destino] != MatrizQ.PARED){
                        posiblesmovimientos[j] = destino;       
                        }
                    }
                
                //calculo los valores de P para cada uno
                
                for (int p = 0; p<posiblesmovimientos.length;p++){
                    
                     tot = 0;
                    
                    tot = ( Math.pow(Math.E,((q_matriz[posiblesmovimientos[p]])/Interfaz.eptau)));
                }
                
                for (int z = 0; z<posiblesmovimientos.length;z++){
                    
                    probabilidades[z] = ( (Math.pow(Math.E,(q_matriz[posiblesmovimientos[z]])/Interfaz.eptau)) );
                    
                }
        
                double ale = 0;
                
                ale = Math.random();
                double acumprob = 0;
                        
                for( int w = 0; w < probabilidades.length; w++){
                    
                    acumprob = acumprob + probabilidades[w];
                    if (ale <= acumprob){
                        moverG(posiblesmovimientos[w]);
                        
                    }  
                
                    
                }
            
            
        }
        
        
        
	private double calculateMaxQvalue()
	{
		
                double[] qArray = new double[8];
		
		int VRindex = getVirtualRobotIndex();
		
		for(int i = 0; i < 7; i++)
		{
			int destination = 0;
			
			if(i == arriba)	    destination = VRindex - Interfaz.x_fila;
                        else if(i == arribaizq)   destination = VRindex - Interfaz.x_fila -1;
                        else if(i == arribader)   destination = VRindex - Interfaz.x_fila +1;
			else if(i == abajo)  destination = VRindex + Interfaz.x_fila;
                        else if(i == abajoizq) destination = VRindex + Interfaz.x_fila -1;
                        else if(i == abajoder) destination = VRindex + Interfaz.x_fila + 1;
			else if(i == izquierda)  destination = VRindex - 1;
			else if(i == derecha) destination = VRindex + 1;
			
			
                            if(v_map[destination] != MatrizQ.PARED)
				qArray[i] = q_matriz[destination];
		}
		
		Arrays.sort(qArray);
		
		return qArray[7];
	}
	
        
        
        
  
    public void qlearning()
	{
		
                // Make initializations
		long startTime = System.currentTimeMillis(); // For time limit control
		//initVirtualMap();
		//initVirtualRobot();
		
		initQMatrix();
		
		for(int i = 0; i < Interfaz.episodios; i++) // Loop as MAX_EPISODE
		{
			initVirtualMap();
			initVirtualRobot();
			
			// While not reached goal state or catched by the danger.
			while(!llegada_vr && !peligro_vr) 
			{
                                
                            switch(Interfaz.politica){
                               case 1: eGreedy();break;
                               case 2: softMax();break;
                               case 3: RandomMove();break;
                            }
                            
				
				int VRindex = getVirtualRobotIndex();
				
				if(peligro_vr)
					q_matriz[VRindex] = matrec.r_matriz[VRindex];
				
				else	
					q_matriz[VRindex] = matrec.r_matriz[VRindex] + Interfaz.gamma * calculateMaxQvalue();
				
				// Control time limit for dead end maps (infinite loops)
				long elapsedTime = System.currentTimeMillis() - startTime;
				
				if(elapsedTime > tiempolimite)
					return;
			}aprendio = true;
		} 
                
                
	}
	

       public double[] getQ_matrix()
	{
		return q_matriz;
                
	}
	
	public double getMaxQMatrixValue()
	{
		for(int i = 0; i < Interfaz.tamaño; i++)
			if(Interfaz.getMapa().map[i] == MatrizQ.FINAL)
				return q_matriz[i];
		
		return 0; // Goal not found
              
	}
        
//       public void utialize()
//	{
//		double[] qArray = new double[8];
//		
//		int Rindex = getRobotIndex();
//                
//		
//		 Find maximum Q value
//		for(int i = 0; i < 7; i++)
//		{
//			int destination = 0;
//			
//			if(i == arriba)	  destination = Rindex - Interfaz.x_fila;
//                        else if(i == arribaizq) destination = Rindex - Interfaz.x_fila +1;
//                        else if(i == arribader) destination = Rindex - Interfaz.x_fila -1;
//			else if(i == abajo)  destination = Rindex + Interfaz.x_fila;
//                        else if(i == abajoizq)  destination = Rindex + Interfaz.x_fila -1;
//                        else if(i == abajoder)  destination = Rindex + Interfaz.x_fila +1;
//			else if(i == izquierda)  destination = Rindex - 1;
//			else if(i == derecha) destination = Rindex + 1;
//                        
//			qArray[i] = q_matrix[destination];
//		}
//		
//		Arrays.sort(qArray);
//		
//		for(int j = 0; j < 7; j++)
//		{
//			int destination = 0;
//			
//			if(j == arriba)	    destination = Rindex - Interfaz.x_fila;
//			else if(j == arribaizq) destination = Rindex - Interfaz.x_fila +1;
//                        else if(j == arribader) destination = Rindex - Interfaz.x_fila -1;
//			else if(j == abajo)  destination = Rindex + Interfaz.x_fila;
//                        else if(j == abajoizq)  destination = Rindex + Interfaz.x_fila -1;
//                        else if(j == abajoder)  destination = Rindex + Interfaz.x_fila +1;
//			else if(j == izquierda)  destination = Rindex - 1;
//			else if(j == derecha) destination = Rindex + 1;
//			
//			 Make moves
//			if(qArray[7] == q_matrix[destination] && j == arriba)
//			{
//				moveUp();
//				return;
//			}
//                        
//                        else if(qArray[7] == q_matrix[destination] && j == arribaizq)
//			{
//				moveUpLeft();
//				return;
//			}
//                        
//                        else if(qArray[7] == q_matrix[destination] && j == arribader)
//			{
//				moveUpRight();
//				return;
//			}
//                        
//			else if(qArray[7] == q_matrix[destination] && j == abajo)
//			{
//				moveDown();
//				return;
//			}
//                        
//                        else if(qArray[7] == q_matrix[destination] && j == abajoizq)
//			{
//				moveDownLeft();
//				return;
//			}
//                        
//                        else if(qArray[7] == q_matrix[destination] && j == abajoder)
//			{
//				moveDownRight();
//				return;
//			}
//                        
//			else if(qArray[7] == q_matrix[destination] && j == izquierda)
//			{
//				moveLeft();
//				return;
//			}
//			else if(qArray[7] == q_matrix[destination] && j == derecha)
//			{
//				moveRight(); 
//				return;
//			}
//                      
//                        
//		}
//	}
//        
//      
// Apply happy end to the map.
//	public void applyHappyEnd()
//	{
//		Interfaz.getMapa().map[getRobotIndex()] = LLEGADAFINAL;
//	}
//	
//	 Apply bad end - unhappy end to the map.
//	public void applyBadEnd()
//	{
//		Interfaz.getMapa().map[getRobotIndex()] = PARED;
//	}
//	
//	public boolean IS_DANGER()
//	{
//		return peligro;
//	}
//
//	public boolean IS_GOAL()
//	{
//		return llegada;
//	}

	

        

}

